﻿using Microsoft.Graphics.Canvas;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media.Protection.PlayReady;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Navigation;
using 纸片人.AttributeUsageFloder;
using Color = Windows.UI.Color;
using Point = Windows.Foundation.Point;
using Size = System.Drawing.Size;



// https://go.microsoft.com/fwlink/?LinkId=234238 上介绍了“空白页”项模板

namespace 纸片人.PageViews
{
    [LastModified("2022-5-12", "create FreeTransformPage.xaml")]
    [PageType("cpu", "自由变换")]
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class FreeTransformPage : Page
    {
        double width;
        double height;
        Color[] colors;
        Color[] tempColors;
        WriteableBitmap wb;
        double m;

        public FreeTransformPage()
        {
            this.InitializeComponent();
        }

        private async void selectPicture_Click(object sender, RoutedEventArgs e)
        {
            CanvasBitmap bitmap = await Function.SelectImageAsync();

            if (bitmap == null)
                return;

            int w, h;

            Function.InitialParameter(bitmap, out w, out h, out colors, out tempColors, image, out wb);

            width = w;
            height = h;

            initialRect();
        }


        Rect orignaliRect = new Rect();

        Rect curRect = new Rect();
        Point oriltThumb = new Point();
        Point orirtThumb = new Point();
        Point orilbThumb = new Point();
        Point orirbThumb = new Point();

        Vector2 imageOffset = new Vector2();
        void initialRect()
        {

            double cw = canvas.ActualWidth;
            double ch = canvas.ActualHeight;
            image.Width = width;
            image.Height = height;
            int w = (int)image.Width;
            int h = (int)image.Height;

            image.Translation = new System.Numerics.Vector3((float)((cw - w) / 2), (float)((ch - h) / 2), 0);


            Point p = image.TransformToVisual(canvas).TransformPoint(new Point());
            imageOffset = p.ToVector2();
            int ts = (int)ltThumb.Height / 2;

            int x = (int)p.X;
            int y = (int)p.Y;

            oriltThumb = new Point(x - ts, y - ts);
            orirtThumb = new Point(x + w - ts, y - ts);
            orilbThumb = new Point(x - ts, y + h - ts);
            orirbThumb = new Point(x - ts + w, y + h - ts);


            ltThumb.Translation = new System.Numerics.Vector3(x - ts, y - ts, 0);
            rtThumb.Translation = new System.Numerics.Vector3(x + w - ts, y - ts, 0);

            lbThumb.Translation = new System.Numerics.Vector3(x - ts, y + h - ts, 0);
            rbThumb.Translation = new System.Numerics.Vector3(x - ts + w, y + h - ts, 0);



            rect.Translation = new System.Numerics.Vector3((float)(x), (float)y, 0);
            rect.Width = image.Width;
            rect.Height = image.Height;
            Point ltxy, rtxy, lbxy, rbxy;
            ltxy = new Point(ltThumb.Translation.X, ltThumb.Translation.Y);
            rtxy = new Point(rtThumb.Translation.X, rtThumb.Translation.Y);
            lbxy = new Point(lbThumb.Translation.X, lbThumb.Translation.Y);
            rbxy = new Point(rbThumb.Translation.X, rbThumb.Translation.Y);
            tempColors = new Color[colors.Length];
            double imageWidth = image.Width;
            double imageHeight = image.Height;
            var b = (bool)perspective.IsChecked;


            FreeTrans3(0, 0, width, height, b, ltxy, rtxy, lbxy, rbxy, imageWidth, imageHeight);
            Function.InitialImage(tempColors, (int)width, (int)height, image, wb);

        }







        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            Thumb thumb = sender as Thumb;

            double ox = e.HorizontalChange;
            double oy = e.VerticalChange;

            double x = thumb.Translation.X;
            double y = thumb.Translation.Y;
            if (thumb == ltThumb)
            {
                ox = x + ox < oriltThumb.X ? oriltThumb.X : x + ox;
                oy = y + oy < oriltThumb.Y ? oriltThumb.Y : y + oy;
            }
            else if (thumb == rtThumb)
            {
                ox = x + ox > orirtThumb.X ? orirtThumb.X : x + ox;
                oy = y + oy < orirtThumb.Y ? orirtThumb.Y : y + oy;
            }

            else if (thumb == lbThumb)
            {
                ox = x + ox < orilbThumb.X ? orilbThumb.X : x + ox;
                oy = y + oy > orilbThumb.Y ? orilbThumb.Y : y + oy;
            }
            else
            {
                ox = x + ox > orirbThumb.X ? orirbThumb.X : x + ox;
                oy = y + oy > orirbThumb.Y ? orirbThumb.Y : y + oy;
            }

            thumb.Translation = new System.Numerics.Vector3((float)ox, (float)oy, 0);

            int xm = (int)width / 12;
            int ym = (int)height;
            int pw = 0;
            int ph = 0;
            List<IAsyncAction> list = new List<IAsyncAction>();
            Point ltxy, rtxy, lbxy, rbxy;
            ltxy = new Point(ltThumb.Translation.X, ltThumb.Translation.Y);
            rtxy = new Point(rtThumb.Translation.X, rtThumb.Translation.Y);
            lbxy = new Point(lbThumb.Translation.X, lbThumb.Translation.Y);
            rbxy = new Point(rbThumb.Translation.X, rbThumb.Translation.Y);
            tempColors = new Color[colors.Length];
            double imageWidth = image.Width;
            double imageHeight = image.Height;
            var b = (bool)perspective.IsChecked;


            //FreeTrans3(0, 0, width, height, b, ltxy, rtxy, lbxy, rbxy, imageWidth, imageHeight);
            //Function.InitialImage(tempColors, (int)width, (int)height, image, wb);
            //return;
            for (int i = 1; i <= 12; i++)
            {

                int sx = pw;
                int sy = ph;
                int ex = xm * i;
                int ey = ym;
                //ex = ex > width - 2 ? width - 2 : ex;
                list.Add(Windows.System.Threading.ThreadPool.RunAsync(p =>
                {
                    FreeTrans(sx, sy, ex, ey, b, ltxy, rtxy, lbxy, rbxy, imageWidth, imageHeight);
                }));

                pw = xm * i;

            }

            if ((int)width % 12 != 0)
            {
                int sx = pw;
                int sy = 0;

                int ex = pw + ((int)width % 12);
                int ey = ym;

                list.Add(Windows.System.Threading.ThreadPool.RunAsync(p =>
                {
                    FreeTrans(sx, sy, ex, ey, b, ltxy, rtxy, lbxy, rbxy, imageWidth, imageHeight);
                }));
            }

            Task.WaitAll(list.Select(temp => temp.AsTask()).ToArray());

            Function.InitialImage(tempColors, (int)width, (int)height, image, wb);


        }

        private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {




        }


        void FreeTrans(double sx, double sy, double ex, double ey, bool isPersp, Point ltThumb, Point rtThumb, Point lbThumb, Point rbThumb, double imageWidth, double imageHeight)
        {
            double cmx = width / imageWidth;
            double cmy = height / imageHeight;

            //左上x-左下x
            double lox = ltThumb.X - lbThumb.X;

            lox = lox * cmy;
            //左上Y-右上y
            double toy = ltThumb.Y - rtThumb.Y;
            toy = toy * cmy;


            //右上x-右下x
            double rox = rtThumb.X - rbThumb.X;
            rox = rox * cmx;

            //左下y-右下y
            double boy = lbThumb.Y - rbThumb.Y;
            boy = boy * cmy;



            double w = 0, h = 0;


            w = width;
            h = height;


            #region 确定点的位置
            //左右上角点
            Point ltPoint = new Point();
            Point rtPoint = new Point();
            //左右下角点
            Point lbPoint = new Point();
            Point rbPoint = new Point();

            ltPoint.X = (ltThumb.X - oriltThumb.X) * cmx;
            ltPoint.Y = (ltThumb.Y - oriltThumb.Y) * cmy;

            rtPoint.X = w + (rtThumb.X - orirtThumb.X) * cmx;
            rtPoint.Y = (rtThumb.Y - orirtThumb.Y) * cmy;

            lbPoint.X = (lbThumb.X - orilbThumb.X) * cmx;
            lbPoint.Y = h + (lbThumb.Y - orilbThumb.Y) * cmy;

            rbPoint.X = w + (rbThumb.X - orirbThumb.X) * cmx;
            rbPoint.Y = h + (rbThumb.Y - orirbThumb.Y) * cmy;

            double ltx = 0, rtx = 0, lty = 0, rty = 0, lbx = 0, lby = 0, rbx = 0, rby = 0;

            ltx = ltPoint.X;
            lty = ltPoint.Y;
            rtx = rtPoint.X;
            rty = rtPoint.Y;

            lbx = lbPoint.X;
            lby = lbPoint.Y;
            rbx = rbPoint.X;
            rby = rbPoint.Y;

            #endregion
            if (!isPersp)
            {
                for (double x = sx; x <= ex; x++)
                {
                    double xm = x / (width - 1);

                    double tx = ltPoint.X + xm * (rtPoint.X - ltPoint.X);
                    double bx = lbPoint.X + xm * (rbPoint.X - lbPoint.X);

                    for (double y = sy; y < ey; y++)
                    {

                        double px = 0, py = 0;


                        double ym = y / (height - 1);


                        double ly = ltPoint.Y + ym * (lbPoint.Y - ltPoint.Y);
                        double ry = rtPoint.Y + ym * (rbPoint.Y - rtPoint.Y);

                        px = tx - ((tx - bx) * ym) * ym;
                        py = ly - ((ly - ry) * xm) * xm;

                        //px = tx - ((tx - bx) * ym);
                        //py = ly - ((ly - ry) * xm);

                        px = px > width - 1 ? width - 1 : px;
                        py = py > height - 1 ? height - 1 : py;


                        try
                        {
                            if ((int)x == 0 || (int)y == 0 || (int)x == (int)width || (int)y == (int)height)
                            {
                                tempColors[(int)px + (int)py * (int)width] = Colors.Black;

                            }
                            else
                            {
                                tempColors[(int)px + (int)py * (int)width] = colors[(int)x + (int)y * (int)width];

                            }

                        }
                        catch
                        {

                        }
                    }



                }
            }
            else
            {
                for (double x = sx; x <= ex; x++)
                {
                    double xm = x / (width - 1);

                    double tx = ltPoint.X + xm * (rtPoint.X - ltPoint.X);
                    double bx = lbPoint.X + xm * (rbPoint.X - lbPoint.X);

                    for (double y = sy; y < ey; y++)
                    {
                        double px = 0, py = 0;

                        double ym = y / (height - 1);

                        double ly = ltPoint.Y + ym * (lbPoint.Y - ltPoint.Y);
                        double ry = rtPoint.Y + ym * (rbPoint.Y - rtPoint.Y);

                        px = tx - ((tx - bx) * ym);
                        py = ly - ((ly - ry) * xm);

                        px = px > width - 1 ? width - 1 : px;
                        py = py > height - 1 ? height - 1 : py;


                        try
                        {
                            if ((int)x == 0 || (int)y == 0 || (int)x == (int)width || (int)y == (int)height)
                            {
                                tempColors[(int)px + (int)py * (int)width] = Colors.Black;

                            }
                            else
                            {
                                tempColors[(int)px + (int)py * (int)width] = colors[(int)x + (int)y * (int)width];

                            }

                        }
                        catch
                        {

                        }
                    }



                }
            }

        }

        void FreeTrans2(double sx, double sy, double ex, double ey, bool isPersp, Point ltThumb, Point rtThumb, Point lbThumb, Point rbThumb, double imageWidth, double imageHeight)
        {
            double cmx = 1;
            double cmy = 1;





            double w = 0, h = 0;


            w = width;
            h = height;


            #region 确定点的位置
            //左右上角点
            Point ltPoint = new Point();
            Point rtPoint = new Point();
            //左右下角点
            Point lbPoint = new Point();
            Point rbPoint = new Point();

            ltPoint.X = (ltThumb.X - oriltThumb.X) * cmx;
            ltPoint.Y = (ltThumb.Y - oriltThumb.Y) * cmy;

            rtPoint.X = w + (rtThumb.X - orirtThumb.X) * cmx;
            rtPoint.Y = (rtThumb.Y - orirtThumb.Y) * cmy;

            lbPoint.X = (lbThumb.X - orilbThumb.X) * cmx;
            lbPoint.Y = h + (lbThumb.Y - orilbThumb.Y) * cmy;

            rbPoint.X = w + (rbThumb.X - orirbThumb.X) * cmx;
            rbPoint.Y = h + (rbThumb.Y - orirbThumb.Y) * cmy;



            #endregion
            //上边长度和下边长度
            double tw = rtPoint.X - ltPoint.X;
            double bw = rbPoint.X - lbPoint.X;
            //左边高度和右边高度
            double lh = lbPoint.Y - ltPoint.Y;
            double rh = rbPoint.Y - rtPoint.Y;
            for (double x = sx; x <= ex; x++)
            {
                double xm = x / (width - 1);
                //左边 移动的距离
                double tx = ltPoint.X + xm * tw;
                double bx = lbPoint.X + xm * bw;
                var ltx = tx;
                var lbx = bx;
                //var rtx = rtPoint.X+
                for (double y = sy; y < ey; y++)
                {
                    double px = 0, py = 0;

                    double ym = y / (height - 1);

                    //移动左上边和右上边的距离
                    double ly = ltPoint.Y + ym * lh;
                    double ry = rtPoint.Y + ym * rh;
                    //水平差值//垂直差值
                    var diffX = tx - bx;
                    var diffY = ly - ry;
                    //开始位置
                    var ppx = tx - diffX * ym;
                    var ppy = ly - diffY * xm;
                    //当前行的点除以当前行的长度
                    var fx = (x - ppx) / tw;
                    if (fx < 0 || fx > 1)
                        continue;
                    else
                    {
                        var ddddc = 1;
                    }
                    var fy = (y - ppy) / lh;
                    if (fy < 0 || fy > 1)
                    {
                        continue;

                    }
                    else
                    {
                        var sdkjg = 1;
                    }
                    ////左向右的移动
                    //var leftToRightMove = (ltPoint.X + (ltPoint.X - lbPoint.X) * ym) * (1f - xm);
                    ////右边向左边的移动
                    //var rightToLeftMove = (width - rtPoint.X + (rtPoint.X - rbPoint.X) * ym) * xm;

                    //var topToBottomMove = (ltPoint.Y + (ltPoint.Y - rtPoint.Y) * xm) * (1f - ym);
                    //var bottomToTopMove = (height - lbPoint.Y + (lbPoint.Y - rbPoint.Y) * xm) * ym;

                    //左向右的移动
                    var leftToRightMove = (ltPoint.X + (ltPoint.X - lbPoint.X) * fy) * (1f - fx);
                    //右边向左边的移动
                    var rightToLeftMove = (width - rtPoint.X + (rtPoint.X - rbPoint.X) * fy) * fx;

                    var topToBottomMove = (ltPoint.Y + (ltPoint.Y - rtPoint.Y) * fx) * (1f - fy);
                    var bottomToTopMove = (height - lbPoint.Y + (lbPoint.Y - rbPoint.Y) * fx) * fy;

                    px = x - leftToRightMove + rightToLeftMove;
                    py = y - topToBottomMove + bottomToTopMove;

                    if (x == 130 && y == 145)
                    {
                        var a = 2;
                    }








                    //var topToBottomMove = ltPoint.Y

                    //double ll = (leftToRightMove - (ltPoint.X - (ltPoint.X - lbPoint.X) * ym)) * (1 - xm);
                    //double rr = (rightToLeftMove - (rtPoint.X - (rtPoint.X - rbPoint.X) * ym)) * xm;
                    //px = x + ll + rr;

                    //double tt = (txy.Y - (ltPoint.Y - (ltPoint.Y - rtPoint.Y) * xm)) * (1 - ym);
                    //double dd = (bxy.Y - (lbPoint.Y - (lbPoint.Y - rbPoint.Y) * xm)) * (ym);
                    //oy = oy + tt + dd;


                    ////var ccc = ((tx - bx) * ym) / (tx - bx);
                    ////var ddd = ltPoint.X + ccc * width;
                    ////px = px > width - 1 ? width - 1 : px;
                    ////py = py > height - 1 ? height - 1 : py;
                    ////px = x - (diffX-(diffX * ym));
                    ////py = y -(diffY- diffY * xm);
                    try
                    {
                        if (false)
                        {
                            tempColors[(int)px + (int)py * (int)width] = Colors.Black;

                        }
                        else
                        {

                            var bnx = x + x - px;
                            var bny = y + y - py;
                            var index = px + py * width;
                            var index2 = (int)x + (int)y * (int)width;
                            if (index > 0 && index < colors.Length)
                            {
                                tempColors[index2] = colors[(int)index];
                            }
                            else
                            {
                                tempColors[index2] = new Color();
                            }
                            //tempColors[(int)px + (int)py * (int)width] = colors[(int)x + (int)y * (int)width];

                        }

                    }
                    catch (Exception ee)
                    {

                    }
                }
            }


        }
        void FreeTrans3(double sx, double sy, double ex, double ey, bool isPersp, Point ltThumb, Point rtThumb, Point lbThumb, Point rbThumb, double imageWidth, double imageHeight)
        {

            var leftTop = ltThumb.ToVector2() - imageOffset + new Vector2(5, 5);//0
            var rightTop = rtThumb.ToVector2() - imageOffset + new Vector2(5, 5);//1
            var leftBottm = lbThumb.ToVector2() - imageOffset + new Vector2(5, 5);//3
            var rightBottom = rbThumb.ToVector2() - imageOffset + new Vector2(5, 5);//2

            var AB = rightTop - leftTop;
            var BC = rightBottom - rightTop;
            var CD = leftBottm - rightBottom;
            var DA = leftTop - leftBottm;

            AB = Vector2.Normalize(AB); ;
            BC = Vector2.Normalize(BC); ;
            CD = Vector2.Normalize(CD); ;
            DA = Vector2.Normalize(DA); ;



            Vector2 ptInPlane = new Vector2();
            var x1 = 0;
            var y1 = 0;

            for (int y = 0; y < imageHeight; y++)
            {
                for (int x = 0; x < imageWidth; x++)
                {
                    Vector2 pos = new Vector2(x, y);
                    if (isOnPlaneABCD(pos,leftTop,rightTop,leftBottm,rightBottom))
                    {
                        var dab = Math.Abs(CrossProduct(pos - leftTop, AB));
                        var dbc = Math.Abs(CrossProduct(pos - rightTop, BC));
                        var dcd = Math.Abs(CrossProduct(pos - rightBottom, CD));
                        var dda = Math.Abs(CrossProduct(pos - leftBottm, DA));

                        ptInPlane.X = (float)(imageWidth * (dda / (dda + dbc)));
                        ptInPlane.Y = (float)(imageHeight * (dab / (dab + dcd)));
                        x1 = (int)ptInPlane.X;
                        y1 = (int)ptInPlane.Y;
                        if (x1 >= 0 && x1 < imageWidth && y1 >= 0 && y1 < imageHeight)
                        {
                            //Debug.WriteLine(x1 + y1 * (int)imageWidth);
                            tempColors[x + y * (int)imageWidth] = colors[x1 + y1 * (int)imageWidth];
                        }
                    }
                }
            }

        }

        private bool isOnPlaneABCD(Vector2 pt,Vector2 ltp,Vector2 rtp,Vector2 lbp,Vector2 rbp) //  including point on border
        {
            if (!IsCCW(pt, ltp, rtp))
            {
                if (!IsCCW(pt, rtp, rbp))
                {
                    if (!IsCCW(pt, rbp, lbp))
                    {
                        if (!IsCCW(pt, lbp, ltp))
                            return true;
                    }
                }
            }
            return false;
        }

        public  bool IsCCW(Vector2 pt1, Vector2 pt2, Vector2 pt3)
        {
            Vector2 V21 = pt1 - pt2;
            Vector2 v23 = pt3 - pt2;
            return (CrossProduct(V21,v23) > 0);  // sin(angle pt2 pt1 pt3) < 0, 180<angle pt2 pt1 pt3 <360
        }

        public double CrossProduct(Vector2 a, Vector2 b)
        {
            return a.X * b.Y - b.X * a.Y;
        }


        public double Magnitude(Vector2 a)
        {
            return Math.Sqrt(a.X * a.X + a.Y * a.Y);
        }

        public Vector2 Division(Vector2 a, double c)
        {
            return new Vector2((float)(a.X / c), (float)(a.Y / c));
        }



        //获取两直线交点，
        public static Vector2 GetIntersection(Vector2 lineAStart, Vector2 lineAEnd, Vector2 lineBStart, Vector2 lineBEnd)
        {
            float x1 = lineAStart.X, y1 = lineAStart.Y;
            float x2 = lineAEnd.X, y2 = lineAEnd.Y;

            float x3 = lineBStart.X, y3 = lineBStart.Y;
            float x4 = lineBEnd.X, y4 = lineBEnd.Y;

            //equations of the form x=c (two vertical lines)
            if (x1 == x2 && x3 == x4 && x1 == x3)
            {
                return Vector2.Zero;
            }

            //equations of the form y=c (two horizontal lines)
            if (y1 == y2 && y3 == y4 && y1 == y3)
            {
                return Vector2.Zero;
            }

            //equations of the form x=c (two vertical lines)
            if (x1 == x2 && x3 == x4)
            {
                return Vector2.Zero;
            }

            //equations of the form y=c (two horizontal lines)
            if (y1 == y2 && y3 == y4)
            {
                return Vector2.Zero;
            }
            float x, y;

            if (x1 == x2)
            {
                float m2 = (y4 - y3) / (x4 - x3);
                float c2 = -m2 * x3 + y3;

                x = x1;
                y = c2 + m2 * x1;
            }
            else if (x3 == x4)
            {
                float m1 = (y2 - y1) / (x2 - x1);
                float c1 = -m1 * x1 + y1;

                x = x3;
                y = c1 + m1 * x3;
            }
            else
            {
                //compute slope of line 1 (m1) and c2
                float m1 = (y2 - y1) / (x2 - x1);
                float c1 = -m1 * x1 + y1;

                //compute slope of line 2 (m2) and c2
                float m2 = (y4 - y3) / (x4 - x3);
                float c2 = -m2 * x3 + y3;

                //solving equations (3) & (4) => x = (c1-c2)/(m2-m1)
                //plugging x value in equation (4) => y = c2 + m2 * x
                x = (c1 - c2) / (m2 - m1);
                y = c2 + m2 * x;

                //          if (!(-m1 * x + y == c1
                //              && -m2 * x + y == c2))
                //          {
                //              return Vector2.Zero;
                //          }
            }

            if (IsInsideLine(lineAStart, lineAEnd, x, y) &&
                IsInsideLine(lineBStart, lineBEnd, x, y))
            {
                return new Vector2(x, y);
            }

            //return default null (no intersection)
            return Vector2.Zero;
        }
        private static bool IsInsideLine(Vector2 start, Vector2 end, float x, float y)
        {
            return ((x >= start.X && x <= end.X)
                || (x >= end.X && x <= start.X))
                && ((y >= start.Y && y <= end.Y)
                    || (y >= end.Y && y <= start.Y));
        }


        private void reset_Click(object sender, RoutedEventArgs e)
        {
            initialRect();
        }
    }
}






